/*
 *  TestCounterPtr.cpp
 *  utility
 *
 *  Created by serika on 10-10-6.
 *  Copyright 2010 serika. All rights reserved.
 *
 */

#include "TestCounterPtr.h"
#include <Utility/CounterPtr.h>
#include <Utility/Exception.h>
#include <vector>
#include <iostream>

namespace serika
{
	namespace test
	{
		class Type1
		{
		public:
			Type1(bool& deleted) : mDeleted(deleted) { mDeleted = false; }
			~Type1()
			{
				std::cout << "delete type1" << std::endl;
				mDeleted = true;
			}
			
			void VoidFunc()
			{
				std::cout << "Type1::mDeleted= " << mDeleted << std::endl;
			}
			
		private:
			bool& mDeleted;
		};
		
		class Type2
		{
		public:
			Type2(bool& deleted) : mDeleted(deleted) { mDeleted = false; }
			~Type2()
			{
				std::cout << "delete type2" << std::endl;
				mDeleted = true;
			}
			
			void VoidFunc()
			{
				std::cout << "Type2::mDeleted= " << mDeleted << std::endl;
			}
			
		private:
			bool & mDeleted;
		};
		
		typedef ut::CounterPtr<Type1> Ptr1;
		typedef ut::CounterPtr<Type2> Ptr2;
		
		CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(TestCounterPtr, "TestUtility");
		
		TestCounterPtr::TestCounterPtr()
		{
		}
		
		TestCounterPtr::~TestCounterPtr()
		{
		}
		
		void TestCounterPtr::TestRunWell()
		{
			bool deleted = false;
			Type1* obj1 = new Type1(deleted);
			Ptr1* p1 = new Ptr1(obj1);			
			Ptr1* p2 = new Ptr1(*p1);
			(*p1)->VoidFunc();
			(*p2)->VoidFunc();
			delete p1;
			CPPUNIT_ASSERT(!deleted);
			(*p2)->VoidFunc();
			delete p2;
			CPPUNIT_ASSERT(deleted);
		}
		
		void TestCounterPtr::TestUseDeletedPtr()
		{
			Ptr1* p1 = 0;
			Ptr1* p2 = 0;
			try
			{
				bool deleted = false;
				Type1* obj1 = new Type1(deleted);
				p1 = new Ptr1(obj1);
				p2 = new Ptr1(*p1);
				
				p1->Delete();
				CPPUNIT_ASSERT(deleted);
				(*p2)->VoidFunc();
			}
			catch (ut::Exception& ex)
			{
				CPPUNIT_ASSERT(false);
			}
			
			if (p1) { delete p1; }
			if (p2) { delete p2; }
		}
		
		void TestCounterPtr::TestTwoKindPtr()
		{
			bool delete1 = false;
			Type1* obj1 = new Type1(delete1);
			Ptr1* p1 = new Ptr1(obj1);
			bool delete2 = false;
			Type2* obj2 = new Type2(delete2);
			Ptr2* p2 = new Ptr2(obj2);
			(*p1)->VoidFunc();
			(*p2)->VoidFunc();
			delete p1;
			CPPUNIT_ASSERT(!delete2);
			CPPUNIT_ASSERT(delete1);
			(*p2)->VoidFunc();
			delete p2;
			CPPUNIT_ASSERT(delete2);
		}
		
		void TestCounterPtr::TestInVector()
		{
			bool delete1 = false;
			Type1* obj1 = new Type1(delete1);
			Ptr1* p1 = new Ptr1(obj1);
			
			std::vector<Ptr1> ps;
			ps.push_back(*p1);
			delete p1;
			CPPUNIT_ASSERT(!delete1);
			ps.front()->VoidFunc();
		}
		
		void TestCounterPtr::TestInVector2()
		{
			bool delete1 = false;
			{
				Type1* obj1 = new Type1(delete1);
				std::vector<Ptr1> ps;
				ps.push_back(obj1);
				ps.front()->VoidFunc();
			}
			CPPUNIT_ASSERT(delete1);
		}
		
		void TestCounterPtr::TestTwoSameKindPtr()
		{
			bool delete1 = false;
			Type1* obj1 = new Type1(delete1);
			Ptr1* p1 = new Ptr1(obj1);
			bool delete2 = false;
			Type1* obj2 = new Type1(delete2);
			Ptr1* p2 = new Ptr1(obj2);
			(*p1)->VoidFunc();
			(*p2)->VoidFunc();
			delete p1;
			CPPUNIT_ASSERT(!delete2);
			CPPUNIT_ASSERT(delete1);
			(*p2)->VoidFunc();
			delete p2;
			CPPUNIT_ASSERT(delete2);
		}
		
		void TestCounterPtr::TestWrapPtrTwice()
		{
			bool delete1 = false;
			Type1* obj1 = new Type1(delete1);
			Ptr1* p1 = new Ptr1(obj1);
			Ptr1* p2 = new Ptr1(obj1);
			(*p1)->VoidFunc();
			(*p2)->VoidFunc();
			delete p1;
			CPPUNIT_ASSERT(!delete1);
			(*p2)->VoidFunc();
			delete p2;
			CPPUNIT_ASSERT(delete1);
		}
		
		void TestCounterPtr::TestSwitchPtr()
		{
			bool delete1 = false;
			Type1* obj1 = new Type1(delete1);
			Ptr1* p1 = new Ptr1(obj1);
			bool delete2 = false;
			Type1* obj2 = new Type1(delete2);
			Ptr1* p2 = new Ptr1(obj2);
			Ptr1 switchPtr = *p1;
			(*p1)->VoidFunc();
			(*p2)->VoidFunc();
			switchPtr->VoidFunc();
			
			delete p1;
			CPPUNIT_ASSERT(!delete1);
			CPPUNIT_ASSERT(!delete2);
			(*p2)->VoidFunc();
			switchPtr->VoidFunc();
			
			switchPtr = *p2;
			(*p2)->VoidFunc();
			switchPtr->VoidFunc();
			CPPUNIT_ASSERT(delete1);
			CPPUNIT_ASSERT(!delete2);
			
			delete p2;
			switchPtr->VoidFunc();
			CPPUNIT_ASSERT(delete1);
			CPPUNIT_ASSERT(!delete2);
		}
	}
}
		
